/*! \file logmodel.cpp
    \brief This file contains implementation for common data objects for ARS execution model.
    \version 1.0

    This model is build from ARS logs. It has a tree structure. Normally root of this tree is
    TreeViewModel object.
*/

#include "treeviewmodel.h"

///////// 'SearchQualification' ////////////////////////////////////////////////////////////////////

SearchQualification::SearchQualification( const QString propertyName, QVariant propertyValue )
{
    m_propertyName = propertyName;
    m_propertyValue = propertyValue;
}

QString SearchQualification::propertyName() const
{
    return m_propertyName;
}

void SearchQualification::setPropertyName( const QString & propertyName )
{
    m_propertyName = propertyName;
}

QVariant SearchQualification::propertyValue() const
{
    return m_propertyValue;
}

void SearchQualification::setPropertyValue( const QVariant & propertyValue )
{
    m_propertyValue = propertyValue;
}

bool SearchQualification::match( ARSExecItem * item ) const
{
    qDebug(qPrintable(m_propertyName));
    if(m_propertyValue.isNull())
        return true;

    return item->property(m_propertyName.toAscii()) == m_propertyValue;
}

///////// 'TreeViewModel' //////////////////////////////////////////////////////////////////////////

TreeViewModel::TreeViewModel( DynamicModel * model, QObject * parent )
            : QAbstractItemModel(parent)
{
    m_model = model;
}

TreeViewModel::~TreeViewModel()
{
    delete m_model;
}

DynamicModel * TreeViewModel::dynamicModel()
{
    return m_model;
}

void TreeViewModel::setDynamicModel(DynamicModel * model)
{
    if(m_model)
        delete m_model;

    m_model = model;
}

bool TreeViewModel::indexLessThen( const QModelIndex & index1, const QModelIndex & index2 )
{
    return itemLessThen(item(index1), item(index2));
}

bool TreeViewModel::itemLessThen( ARSExecItem * item1, ARSExecItem * item2 )
{
    if(!item1)
        qDebug("TreeViewModel::itemIsMore: item1 is 0");

    if(!item2)
        qDebug("TreeViewModel::itemIsMore: item2 is 0");

    if(item1 == item2)
        return false;

    ARSExecItemList item1Path = itemPath(item1);
    ARSExecItemList item2Path = itemPath(item2);

    for(int i = 0; i < item1Path.size() && i < item2Path.size(); i++)
    {
        if(item1Path.at(i) != item2Path.at(i))
        {
            return item1Path.at(i)->row() < item2Path.at(i)->row();
        }
    }

    return item1Path.size() < item2Path.size();
}

void TreeViewModel::sortIndexList( QModelIndexList * list )
{
    qSort(list->begin(), list->end(), indexLessThen);
}

int TreeViewModel::insertIndexInList( QModelIndexList * list, const QModelIndex & index )
{
    for(int i = 0; i < list->size(); i++)
        if(indexLessThen(index, list->at(i)))
        {
            list->insert(i, index);
            return i;
        }

    return list->size();
}

ARSExecItemList TreeViewModel::itemPath( ARSExecItem * item )
{
    ARSExecItem * tmpItem = item;
    ARSExecItemList path;

    while(tmpItem)
    {
        path.prepend(tmpItem);
        tmpItem = tmpItem->parent();
    }

    return path;
}

void TreeViewModel::setTreeViewHeight( int height )
{
    m_treeViewHeight = height;
    emit layoutChanged();
}

QModelIndex TreeViewModel::index( int row, int column,
                                 const QModelIndex & parent ) const
{
    if(!hasIndex(row, column, parent))
        return QModelIndex();

    if(column != 0)
        return QModelIndex();

     const ARSExecItem * parentItem;

     if (!parent.isValid())
         parentItem = m_model->rootItem();
     else
         parentItem = static_cast<ARSExecItem*>(parent.internalPointer());

     ARSExecItem * childItem = parentItem->child(row);

     if(childItem)
         return createIndex(row, column, childItem);
     else
         return QModelIndex();
}

QModelIndex TreeViewModel::index( ARSExecItem * item ) const
{
    if(!item || (item->type() == ARSExecItem::ItemTypeModelRoot))
        return QModelIndex();

    return index(item->row(), 0, index(item->parent()));
}

ARSExecItem * TreeViewModel::item( const QModelIndex & index )
{
    if(!index.isValid())
        return 0;

    return index.data(TreeViewModel::RoleItemData).value<ARSExecItem *>();
}

QModelIndex TreeViewModel::parent( const QModelIndex & index ) const
{
    if (!index.isValid())
        return QModelIndex();

    ARSExecItem * childItem = static_cast<ARSExecItem *>(index.internalPointer());
    ARSExecItem * parentItem = childItem->parent();

    if (parentItem == m_model->rootItem())
        return QModelIndex();

    return createIndex(parentItem->row(), 0, parentItem);
}

//QModelIndex TreeViewModel::nextIndex( const QModelIndex & index )
//{
//    ARSExecItem * currentItem = item(index);
//    if(!currentItem)
//        return QModelIndex();

//    return index(currentItem->previousItem());
//}

//QModelIndex TreeViewModel::previousIndex( const QModelIndex & index )
//{
//    int itemRow = row();

//    if(itemRow == 0)
//        return index.parent();

//    QModelIndex prevIndex = index.sibling(itemRow - 1, 0);
//    while(prevIndex.child(0, 0).isValid())
//        prevIndex = prevIndex.child(prevIndex.c ->child(prevItem->childCount() - 1);

//    return prevItem;
//}

int TreeViewModel::rowCount( const QModelIndex & parent ) const
{
     const ARSExecItem * parentItem;

     if (!parent.isValid())
         parentItem = m_model->rootItem();
     else
         parentItem = static_cast<ARSExecItem *>(parent.internalPointer());

     return parentItem->childCount();
}

int TreeViewModel::columnCount( const QModelIndex & parent ) const
{
    return 1;
}

QVariant TreeViewModel::data( const QModelIndex & index, int role ) const
{
    if(!index.isValid())
        return QVariant();

    if(role == RoleItemData)
    {
        ARSExecItem * item = static_cast<ARSExecItem *>(index.internalPointer());
        return QVariant::fromValue(item);
    }

    if(role == Qt::SizeHintRole)
        return QSize(0, m_treeViewHeight);

    return QVariant();
}

bool TreeViewModel::setData( const QModelIndex & index,
                            const QVariant & value, int role )
{
    if (!index.isValid() || role != Qt::EditRole || !value.canConvert(QVariant::String))
        return false;

    ARSExecItem * execItem = item(index);

    execItem->setComment(value.toString());
    emit layoutChanged();
    return true;
}

bool TreeViewModel::insertRows(int row, int count, const QModelIndex &parent)
{
    return true;
}

Qt::ItemFlags TreeViewModel::flags( const QModelIndex &index ) const
{
    if (!index.isValid())
        return Qt::ItemIsEnabled;

    return QAbstractItemModel::flags(index);
}

QModelIndex TreeViewModel::search( const SearchQualificationList qualification,
                                  ARSExecItem * item,
                                  SearchDirection direction )
{
    while(item)
    {
        bool isMatched = true;

        for(int i = 0; i < qualification.size() && isMatched; i++)
            isMatched = qualification.at(i).match(item);

        if(isMatched)
            return index(item);

        if(direction == Forward)
            item = item->nextItem();
        else
            item = item->previousItem();
    }

    return QModelIndex();
}

ObjectsIndex * TreeViewModel::index() const
{
    return m_model->index();
}

///////// Class 'TreeViewFilterProxyModel' implementation /////////////////////////////////////////////

TreeViewFilterProxyModel::TreeViewFilterProxyModel( FilterMode filterMode, QObject * parent )
                     : QSortFilterProxyModel(parent)
{
    m_showEmptyWorkflow = true;
    m_docViewActive = false;
    m_actionsMask = static_cast<ARSExecAction::ActionType>(0xffffffff);
    m_filterMode = filterMode;
}

bool TreeViewFilterProxyModel::filterAcceptsRow( int source_row,
                                              const QModelIndex & source_parent ) const
{
    if(m_filterMode == ShowAllWorkflow)
        return true;

    ARSExecItem * item =
            source_parent.child(source_row,
                                0).data(TreeViewModel::RoleItemData).value<ARSExecItem *>();

    if(!item)
        return true;

    return itemAccepted(item);
}

bool TreeViewFilterProxyModel::itemAccepted( ARSExecItem * item ) const
{
    if(m_filterMode == HideEmptyWorkflow)
        if(item->hasChildren() || (item->type() != ARSExecItem::ItemTypeWorkflowObject))
            return true;
        else
            return false;

    if(m_filterMode == HideInternalWorkflow)
        if(item->type() == ARSExecItem::ItemTypeWorkflowBlock)
            return true;
        else
            return isExternal(item);

    return true;
}

void TreeViewFilterProxyModel::setShowEmptyWorkflow( bool show )
{
    if(m_showEmptyWorkflow == show)
        return;

    m_showEmptyWorkflow = show;
    invalidateFilter();
}

void TreeViewFilterProxyModel::setActionsFilter( ARSExecAction::ActionType actions )
{
    if(m_actionsMask == actions)
        return;

    m_actionsMask = actions;
    invalidateFilter();
}

void TreeViewFilterProxyModel::setDocViewActive( bool active )
{
    if(m_docViewActive == active)
        return;

    m_docViewActive = active;
    invalidateFilter();
}

bool TreeViewFilterProxyModel::isExternal(ARSExecItem * item) const
{
    if(!item->findChildren(ARSExecItem::ItemTypeWorkflowBlock).isEmpty())
        return true;

    bool childIsExternal = false;
    for(int i = 0; i < item->childCount() && !childIsExternal; i++)
        if(isExternal(item->child(i)))
            return true;

    return false;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
